package com.yeziji.utils;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.yeziji.common.CommonSymbol;
import com.yeziji.constant.IntBoolean;
import com.yeziji.constant.VariousStrPool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

/**
 * 数据处理工具
 *
 * @author hwy
 * @since 2023/11/12 17:06
 **/
@Slf4j
public class DataUtils {
    /**
     * 记录拷贝 bean 的缓存
     */
    private static final Cache<String, BeanCopier> CONVERT_RECORD_BEAN_COPIER_CACHE =
            CacheBuilder.newBuilder()
                    .initialCapacity(5)
                    .maximumSize(500)
                    .build();

    /**
     * 基本类型与包装类映射
     */
    private static final List<Class<?>> PRIMITIVE_WRAPPER_TYPES = Arrays.asList(
            Boolean.class, Boolean.TYPE,
            Integer.class, Integer.TYPE,
            Long.class, Long.TYPE,
            Float.class, Float.TYPE);

    /**
     * 将数据转换至基本类型
     *
     * @param data 数据
     * @param type 指定类型
     * @return {@link Object}
     */
    public static Object convertToBasicType(Object data, Class<?> type) {
        if (PRIMITIVE_WRAPPER_TYPES.contains(type)) {
            if (type == Boolean.class || type == boolean.class) {
                return convertToBoolean(data);
            } else if (type == Integer.class || type == int.class) {
                return convertToInt(data);
            } else if (type == Long.class || type == long.class) {
                return convertToLong(data);
            } else if (type == Float.class || type == float.class) {
                return convertToFloat(data);
            } else {
                throw new IllegalArgumentException("Unsupported primitive or wrapper type: " + type.getName());
            }
        } else {
            throw new IllegalArgumentException("Unsupported target type: " + type.getName());
        }
    }

    /**
     * 不使用转换器进行拷贝
     *
     * @see #convertBean(Object, Object)
     */
    public static <O, T> T onlyConvertBean(O source, Class<T> targetClass) {
        try {
            return convertBean(source, targetClass.getDeclaredConstructor().newInstance());
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                 NoSuchMethodException e) {
            log.error("onlyConvertBean: 实例化对象失败: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 通过 source copy 实例化一个新的对象
     *
     * @param source      源对象
     * @param targetClass 目标类型
     * @param <O>         原对象泛型
     * @param <T>         目标类泛型
     * @return {@link T} 新的目标类型对象
     */
    public static <O, T> T convertBean(O source, Class<T> targetClass) {
        try {
            return convertBean(source, targetClass.getDeclaredConstructor().newInstance(), new BeanCopierDefaultConverter());
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                 NoSuchMethodException e) {
            log.error("convertBean: 实例化对象失败: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 通过 source copy 实例化一个新的对象
     *
     * @param source      源对象
     * @param targetClass 目标类型
     * @param converter   拷贝转换器
     * @param <O>         原对象泛型
     * @param <T>         目标类泛型
     * @return {@link T} 新的目标类型对象
     */
    public static <O, T> T convertBean(O source, Class<T> targetClass, Converter converter) {
        try {
            return convertBean(source, targetClass.getDeclaredConstructor().newInstance(), converter);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                 NoSuchMethodException e) {
            log.error("convertBean: 实例化对象失败: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 无转换器浅拷贝 bean
     *
     * @see #convertBean(Object, Object, Converter)
     */
    public static <O, T> T convertBean(O source, T target) {
        return convertBean(source, target, null);
    }

    /**
     * 浅拷贝 bean
     *
     * @param source    源对象
     * @param target    目标对象
     * @param converter 拷贝转换器
     * @param <O>       原对象泛型
     * @param <T>       目标类泛型
     * @return {@link T} 目标对象
     */
    public static <O, T> T convertBean(O source, T target, Converter converter) {
        if (source == null || target == null) {
            return target;
        }

        // 获取 copy 缓存键值
        Class<?> targetClass = target.getClass();
        Class<?> sourceClass = source.getClass();
        String copierCacheKey = genKey(sourceClass, targetClass);
        try {
            BeanCopier beanCopier =
                    CONVERT_RECORD_BEAN_COPIER_CACHE.get(
                            copierCacheKey,
                            () -> BeanCopier.create(sourceClass, targetClass, converter != null)
                    );
            // 初始化目标对象
            beanCopier.copy(source, target, converter);
            // copy 赋值
            return target;
        } catch (Exception e) {
            log.error("{} 对象转换异常: {} -> {}", copierCacheKey, sourceClass.getName(), targetClass.getName());
            return null;
        }
    }

    /**
     * 将 map 转换为 pojo 对象
     *
     * @param source map
     * @param clazz  pojo 类型
     * @param <T>    泛型
     * @return pojo
     */
    public static <T> T getObjectByMap(Map<String, Object> source, Class<T> clazz) {
        try {
            // 进行对象构造
            T target = clazz.getDeclaredConstructor().newInstance();
            // 获取字段信息
            Field[] declaredFields = clazz.getDeclaredFields();
            // 遍历字段进行赋值
            for (Field field : declaredFields) {
                field.setAccessible(true);
                String name = field.getName();
                if (Objects.nonNull(source.get(name))) {
                    Object obj = source.get(name);
                    if (obj instanceof Integer && field.getType().equals(Long.class)) {
                        field.set(target, Long.parseLong(String.valueOf(obj)));
                    } else {
                        field.set(target, obj);
                    }
                }
            }
            return target;
        } catch (InstantiationException e) {
            log.error("无参构造失败");
        } catch (IllegalAccessException e) {
            log.error("实体类：" + clazz + "构造或传参出现异常");
        } catch (InvocationTargetException e) {
            log.error("实体类：" + clazz + "获取目标失败");
        } catch (NoSuchMethodException e) {
            log.error("实体类：" + clazz + "没有构造方法");
        }
        return null;
    }

    /**
     * 将 pojo 转换为 map
     *
     * @param obj pojo
     * @return map
     */
    public static Map<String, Object> getMapFromObject(Object obj) {
        Class<?> clazz = obj.getClass();
        Field[] declaredFields = clazz.getDeclaredFields();
        // 查询父类字段
        Class<?> superclass = clazz.getSuperclass();
        if (!Objects.equals(superclass.getName(), Object.class.getName())) {
            Field[] superDeclaredFields = superclass.getDeclaredFields();
            // 数组扩容
            Field[] newFields = Arrays.copyOf(declaredFields, declaredFields.length + superDeclaredFields.length);
            // 拷贝赋值
            System.arraycopy(superDeclaredFields, 0, newFields, declaredFields.length, superDeclaredFields.length);
            // 重新回传
            declaredFields = newFields;
        }

        Map<String, Object> map = new HashMap<>(declaredFields.length);
        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);
            try {
                map.put(declaredField.getName(), declaredField.get(obj));
            } catch (IllegalAccessException e) {
                log.error("赋值异常: {}", e.getMessage(), e);
            }
        }
        return map;
    }

    /**
     * 截取重定向的数据
     *
     * @param uri (a=b&c=d&e=f)
     * @return {a:b,c:d,e:f}
     */
    public static Map<String, String> getQueryParam(String uri) {
        Map<String, String> map = new HashMap<>(0);
        String[] strArr;
        strArr = uri.substring(uri.indexOf(CommonSymbol.AND_SYMBOL))
                .replaceFirst(CommonSymbol.AND_SYMBOL, VariousStrPool.EMPTY)
                .split(CommonSymbol.AND_SYMBOL);
        for (String str : strArr) {
            String[] tempArr;
            tempArr = str.split(CommonSymbol.EQUALS_SIGN);
            if (tempArr.length > 1) {
                map.put(tempArr[0], tempArr[1]);
            }
        }
        return map;
    }

    /**
     * 截取 Url Query
     *
     * @param uri 含参 url {@code url=http://www.xxx.com?a=b&c=d ..}
     * @return query 键值对 {@code { a: b, c: d } }
     */
    public static Map<String, String> getQueryByUrl(String uri) {
        Map<String, String> map = new HashMap<>(0);
        String[] strArr;
        strArr = uri.substring(uri.indexOf(CommonSymbol.QUESTION) + 1).split(CommonSymbol.AND_SYMBOL);
        for (String str : strArr) {
            String[] tempArr;
            tempArr = str.split(CommonSymbol.EQUALS_SIGN);
            if (tempArr.length > 1) {
                map.put(tempArr[0], tempArr[1]);
            }
        }
        return map;
    }

    /**
     * 生成记录转换的键值
     *
     * @param sourceClass 转换源
     * @param targetClass 目标源
     * @return {@link String} xx#xx
     */
    private static String genKey(Class<?> sourceClass, Class<?> targetClass) {
        return sourceClass.getName() + CommonSymbol.NUMBER_SIGN + targetClass.getName();
    }

    /**
     * BeanCopier 默认调用的转换器
     */
    public static class BeanCopierDefaultConverter implements Converter {
        @Override
        public Object convert(Object value, Class targetClass, Object context) {
            // value Integer -> target Long
            if (value instanceof Integer && Objects.equals(targetClass, Long.class)) {
                return ((Integer) value).longValue();
            }
            // value Long -> target Integer
            if (value instanceof Long && Objects.equals(targetClass, Integer.class)) {
                return ((Long) value).intValue();
            }
            // value Integer -> target Boolean
            if (value instanceof Integer && Objects.equals(targetClass, Boolean.class)) {
                return IntBoolean.isTruth((Integer) value);
            }
            // value Boolean -> target Integer (true: 1, false: 0)
            if (value instanceof Boolean && Objects.equals(targetClass, Integer.class)) {
                return ((Boolean) value) ? 1 : 0;
            }
            return value;
        }
    }

    /**
     * data -> boolean
     */
    private static Boolean convertToBoolean(Object data) {
        if (data instanceof Boolean) {
            return (Boolean) data;
        } else if (data instanceof String) {
            return Boolean.parseBoolean((String) data);
        } else {
            throw new IllegalArgumentException("Cannot convert to Boolean: " + data);
        }
    }

    /**
     * data -> int
     */
    private static Integer convertToInt(Object data) {
        if (data instanceof Integer) {
            return (Integer) data;
        } else if (data instanceof String) {
            return Integer.parseInt((String) data);
        } else {
            throw new IllegalArgumentException("Cannot convert to Integer: " + data);
        }
    }

    /**
     * data -> long
     */
    private static Long convertToLong(Object data) {
        if (data instanceof Long) {
            return (Long) data;
        } else if (data instanceof String) {
            return Long.parseLong((String) data);
        } else {
            throw new IllegalArgumentException("Cannot convert to Long: " + data);
        }
    }

    /**
     * data -> float
     */
    private static Float convertToFloat(Object data) {
        if (data instanceof Float) {
            return (Float) data;
        } else if (data instanceof String) {
            return Float.parseFloat((String) data);
        } else {
            throw new IllegalArgumentException("Cannot convert to Float: " + data);
        }
    }
}